//--------------------------------------------------------------------------
// 
//  Copyright (c) Chili Software.  All rights reserved. 
// 
//  File: PersistingTrace.cs
//
//  Description: Stores information about recently saved objects.
// 
//--------------------------------------------------------------------------

using System;
using System.Collections.Generic;

namespace Chili.Opf3
{
	/// <summary>
	/// Class that stores information about recently saved persistent objects.
	/// </summary>
	/// <remarks>
	/// This class stores information about recently saved persistent objects. When saving a persistent object
	/// and it's full object graph (<see cref="ObjectContext.PersistChanges&lt;T&gt;(T, PersistDepths)">PersistChanges</see>)
	/// it's required to avoid circles in the saving process. PersistentTrace saves therefore information about
	/// the objects that have been saved during the current persist operation to avoid those circles.
	/// </remarks>
	public sealed class PersistingTrace
	{
		#region TracedItem

		/// <summary>
		/// Represents a persistent object that is traced.
		/// </summary>
		private struct TracedItem
		{
			private object _target;
			private PersistDepths _depth;

			/// <summary>
			/// Creates a new instance of the <see cref="TracedItem">TracedItem Class</see>.
			/// </summary>
			/// <param name="target">The target that is watched.</param>
			/// <param name="depth">The depth of the current persist.</param>
			public TracedItem(object target, PersistDepths depth)
			{
				_target = target;
				_depth = depth;
			}

			/// <summary>
			/// Returns the target.
			/// </summary>
			public object Target
			{
				get { return _target; }
			}

			/// <summary>
			/// Returns the depth of the current persist.
			/// </summary>
			public PersistDepths Depth
			{
				get { return _depth; }
			}
		}

		#endregion

		private IDictionary<object, TracedItem> _hoppedBy = new Dictionary<object, TracedItem>();
		private IDictionary<object, TracedItem> _persisted = new Dictionary<object, TracedItem>();

		/// <summary>
		/// Creates a new instance of the <see cref="PersistingTrace">PersistingTrace Class</see>.
		/// An instance of this class is only created by the framework itself.
		/// </summary>
		internal PersistingTrace()
		{
		}

		/// <summary>
		/// Adds an object to be traced as "hopped by" by this class. Persistent objects that are not
		/// saved, but their node has been visited are added by using this method.
		/// Classes implementing the <see cref="ICustomPersister">ICustomPersister</see> interface should only add objects
		/// if they save them directly to the storage without using the <see cref="ObjectContext">ObjectContext</see>.
		/// See the <see cref="ICustomPersister">ICustomPersister</see> interface for more information.
		/// </summary>
		/// <param name="obj">The object that is added to be traced by this instance of the <see cref="PersistingTrace">PersistingTrace</see> class.</param>
		/// <param name="depth">The depth of the current persist.</param>
		public void MarkHoppedBy(object obj, PersistDepths depth)
		{
			// Return if the persisted list contains already the item.
			if (_persisted.ContainsKey(obj))
				return;

			if (!_hoppedBy.ContainsKey(obj))
				_hoppedBy.Add(obj, new TracedItem(obj, depth));
		}

		/// <summary>
		/// Adds an object to be traced as "persisted" by this class. Persistent objects that are not
		/// saved, but their node has been visited are added by using this method.
		/// Classes implementing the <see cref="ICustomPersister">ICustomPersister</see> interface should only add objects
		/// if they save them directly to the storage without using the <see cref="ObjectContext">ObjectContext</see>.
		/// See the <see cref="ICustomPersister">ICustomPersister</see> interface for more information.
		/// </summary>
		/// <param name="obj">The object that is added to be traced by this instance of the <see cref="PersistingTrace">PersistingTrace</see> class.</param>
		/// <param name="depth">The depth of the current persist.</param>
		public void MarkPersisted(object obj, PersistDepths depth)
		{
			// Move the object over from the hoppedby list.
			if (_hoppedBy.ContainsKey(obj))
				_hoppedBy.Remove(obj);
			_persisted.Add(obj, new TracedItem(obj, depth));
		}

		/// <summary>
		/// Returns whether the persistent object is already in the "hopped by" list of this class.
		/// </summary>
		/// <param name="obj">The object that is checked of being already traced.</param>
		/// <param name="depth">The depth of the current persist.</param>
		/// <returns>True when the persistent object is already traced by this instance 
		/// of the <see cref="PersistingTrace">PersistingTrace</see> class.</returns>
		public bool ContainsHoppedBy(object obj, PersistDepths depth)
		{
			return _hoppedBy.ContainsKey(obj);
		}

		/// <summary>
		/// Returns whether the persistent object is already in the "persisted" list of this class.
		/// </summary>
		/// <param name="obj">The object that is checked of being already traced.</param>
		/// <param name="depth">The depth of the current persist.</param>
		/// <returns>True when the persistent object is already traced by this instance 
		/// of the <see cref="PersistingTrace">PersistingTrace</see> class.</returns>
		public bool ContainsPersisted(object obj, PersistDepths depth)
		{
			return _persisted.ContainsKey(obj);
		}

		/// <summary>
		/// Clears the cache of the <see cref="PersistingTrace">PersistingTrace</see>.
		/// </summary>
		internal void Clear()
		{
			_hoppedBy.Clear();
			_persisted.Clear();
		}

		/// <summary>
		/// Returns the amount of objects managed by this instance.
		/// </summary>
		internal int Count
		{
			get { return _hoppedBy.Count + _persisted.Count; }
		}
	}
}
